// Copyright (c) Lawrence Livermore National Security, LLC and other VisIt
// Project developers.  See the top-level LICENSE file for dates and other
// details.  No copyright assignment is required to contribute to VisIt.

// ************************************************************************* //
//                             VisWinPathTracker.C                           //
// ************************************************************************* //

#include <VisWinPathTracker.h>

#include <VisWindow.h>

#include <string>
#include <vector>
#include <map>

using std::string;
using std::vector;
using std::map;

//
// Storage for static data elements.
//

VisWinPathTracker *VisWinPathTracker::instance=0;

// ****************************************************************************
//  Method: FindLastSlash
//
//  Purpose:  Searches backward through a string for a slash, either
//            unix-style or windows-style.
//
//  Returns:  The position of the slash, string::npos if not found.
//
//  Arguments:
//     subject  The string to search.
//
//  Programmer: Kathleen Bonnell
//  Creation:   February 13, 2008
//
// ****************************************************************************
int
FindLastSlash(const string &subject)
{
    size_t idx = subject.rfind("/");
    if (idx == string::npos)
        idx = subject.rfind("\\");
    return (int)idx;
}


// ****************************************************************************
//  Method: VisWinPathTracker constructor
//
//  Programmer: Cyrus Harrison
//  Creation:   Sunday June 17, 2007
//
// ****************************************************************************

VisWinPathTracker::VisWinPathTracker()
{
    ;
}

// ****************************************************************************
//  Method: VisWinPathTracker destructor
//
//  Programmer: Cyrus Harrison
//  Creation:   Sunday June 17, 2007
//
// ****************************************************************************

VisWinPathTracker::~VisWinPathTracker()
{
   ;
}

// ****************************************************************************
//  Method: VisWinPathTracker::Instance
//
//  Purpose:
//    Returns a pointer to the singleton instance of the VisWinPathTracker
//    class.
//
//  Programmer: Cyrus Harrison
//  Creation:   Sunday June 17, 2007
//
// ****************************************************************************

VisWinPathTracker *
VisWinPathTracker::Instance()
{
    // If the singleton instance has not been instantiated, create it.
    if (instance == 0)
    {
        instance = new VisWinPathTracker;
    }

    return instance;
}

void VisWinPathTracker::DeleteInstance() {
  delete instance;
  instance = NULL;
}

// ****************************************************************************
//  Method: VisWinPathTracker::AddPath
//
//  Purpose:
//    Adds a reference to the passed path.
//
//  Programmer: Cyrus Harrison
//  Creation:   Sunday June 17, 2007
//
// ****************************************************************************

void
VisWinPathTracker::AddPath(const std::string &path)
{
    std::map<std::string, Entry>::iterator itr;
    itr = entires.find(path);
    // if path exists, increment its counter,
    if(itr != entires.end())
    {
        entires[path].IncrementRefCount();
    }
    else // otherwise create new map entry with ref count of one
    {
        // create a new path entry
        entires[path] = Entry(path);
        // update smart paths
        UpdatePaths();
    }
}

// ****************************************************************************
//  Method: VisWinPathTracker::RemovePath
//
//  Purpose:
//    Removes a reference to the passed path.
//
//  Programmer: Cyrus Harrison
//  Creation:   Sunday June 17, 2007
//
// ****************************************************************************

void
VisWinPathTracker::RemovePath(const std::string &path)
{
    // see if the path has an entry
    std::map<std::string, Entry>::iterator itr;
    itr = entires.find(path);
    if(itr != entires.end())
    {
        // if path exists, decrement its counter
        if(!itr->second.DecrementRefCount())
        {
            // ref count == 0, remove from active paths and update paths
            entires.erase(path);
            UpdatePaths();
        }
    }
}

// ****************************************************************************
//  Method: VisWinPathTracker::GetFileName
//
//  Purpose:
//    Gets the file name of the input path
//
//  Programmer: Cyrus Harrison
//  Creation:   Sunday June 17, 2007
//
// ****************************************************************************

std::string
VisWinPathTracker::GetFileName(const std::string &path)
{
    // get the path's entry
    std::map<std::string, Entry>::iterator itr;
    itr = entires.find(path);
    if(itr != entires.end())
    {
        // return the smart path
        return entires[path].GetFileName();
    }
    // if the path does not have an entry, simply return the full path
    return path;
}

// ****************************************************************************
//  Method: VisWinPathTracker::GetDirectory
//
//  Purpose:
//    Gets the directory of the input path
//
//  Programmer: Cyrus Harrison
//  Creation:   Sunday June 17, 2007
//
// ****************************************************************************

std::string
VisWinPathTracker::GetDirectory(const std::string &path)
{
    // get the path's entry
    std::map<std::string, Entry>::iterator itr;
    itr = entires.find(path);
    if(itr != entires.end())
    {
        // return the smart path
        return entires[path].GetDirectory();
    }
    // if the path does not have an entry, simply return the full path
    return path;
}


// ****************************************************************************
//  Method: VisWinPathTracker::GetSmartPath
//
//  Purpose:
//    Gets the smart expanded path for the input path
//
//  Programmer: Cyrus Harrison
//  Creation:   Sunday June 17, 2007
//
// ****************************************************************************

std::string
VisWinPathTracker::GetSmartPath(const std::string &path)
{
    // get the path's entry
    std::map<std::string, Entry>::iterator itr;
    itr = entires.find(path);
    if(itr != entires.end())
    {
        // return the smart path
        return entires[path].GetSmartPath();
    }
    // if the path does not have an entry, simply return the full path
    return path;
}


// ****************************************************************************
//  Method: VisWinPathTracker::GetSmartDirectory
//
//  Purpose:
//    Gets the smart expanded directory for the input path
//
//  Programmer: Cyrus Harrison
//  Creation:   Tuesday September 25, 2007
//
// ****************************************************************************

std::string
VisWinPathTracker::GetSmartDirectory(const std::string &path)
{
    // get the path's entry
    std::map<std::string, Entry>::iterator itr;
    itr = entires.find(path);
    if(itr != entires.end())
    {
        // return the smart path
        return entires[path].GetSmartDirectory();
    }
    // if the path does not have an entry, simply return the full path
    return path;
}

// ****************************************************************************
//  Method: VisWinPathTracker::UpdatePaths
//
//  Purpose:
//    Construct a smart path for all tracked paths.
//
//  Programmer: Cyrus Harrison
//  Creation:   Sunday June 17, 2007
//
//  Modifications:
//
//    Cyrus Harrison, Tue Sep 25 09:54:17 PDT 2007
//    Added construction of the smart directory
//
//    Kathleen Bonnell, Thu Dec  6 09:34:01 PST 2007 
//    Changed "/" to SLASH_STRING for windows compatibility. 
//
//    Kathleen Bonnell, Wed Feb 13 07:52:58 PST 2008 
//    Call FindLastSlash, which searches for unix and windows style slashes.
//
//    Kathleen Biagas, Wed Jan 29 15:21:21 MST 2014
//    Check size before taking substring.
//
// ****************************************************************************

void
VisWinPathTracker::UpdatePaths()
{
    std::map<std::string, Entry>::iterator itr;
    
    std::string spath;
    std::string sdir;
    
    stringVector paths;
    stringVector dirs;

    // construct a list of paths
    for ( itr = entires.begin(); itr != entires.end(); ++itr)
        paths.push_back(itr->second.GetPath());

    // construct a list of paths
    for ( itr = entires.begin(); itr != entires.end(); ++itr)
        dirs.push_back(itr->second.GetDirectory());

    
    std::string path_common = GetCommonPath(paths);
    std::string dir_common = GetCommonPath(dirs);
    
    int path_common_size = path_common.size();
    int dir_common_size  = dir_common.size();
    
    // compute the smart paths
    for ( itr = entires.begin(); itr != entires.end(); ++itr)
    {
        if( path_common == itr->second.GetPath())
            spath = itr->second.GetFileName();
        else
        {
            if ((size_t)path_common_size < itr->second.GetPath().size())
                spath = itr->second.GetPath().substr(path_common_size+1);
            else
                spath = itr->second.GetPath();
        }

        itr->second.SetSmartPath(spath);
    }
    
    // compute the smart paths
    for ( itr = entires.begin(); itr != entires.end(); ++itr)
    {
        if( dir_common == itr->second.GetDirectory())
            sdir = dir_common.substr(FindLastSlash(dir_common)+1);
        else
        {
            if ((size_t)dir_common_size < itr->second.GetDirectory().size())
                sdir = itr->second.GetDirectory().substr(dir_common_size+1);
            else
                sdir = itr->second.GetDirectory();
        }

        itr->second.SetSmartDirectory(sdir);
    }
}

//*****************************************************************************
//  Method: VisWinPathTracker::GetSubPath
//
//  Purpose:
//    Gets the a path string at a given "depth". Starts at the beginning
//    of the string and uses ith '/' to terminate the subpath.
//
//  Programmer: Cyrus Harrison
//  Creation:   June 14, 2007
//
//  Modifications:
//
//    Cyrus Harrison, Mon Oct  1 11:39:47 PDT 2007
//    Switched from right path to left path to support GetCommonPath.
//
//    Kathleen Bonnell, Thu Dec  6 09:34:01 PST 2007 
//    Changed '/' to SLASH_CHAR for windows compatibility. 
//
//    Kathleen Bonnell, Wed Feb 13 07:52:58 PST 2008 
//    Check for both unix and windows style slashes.
//
// ****************************************************************************

std::string VisWinPathTracker::GetSubPath(const std::string &path,
                                          int depth)
{
    int i = 0;
    // start depth
    int cdepth = -1;
    // current string pos
    int pos   = 0;
    // string pause
    int size = path.size();

    for ( i = 0; i < size && cdepth != depth; i++)
    {
        // if we encounter a slash, update depth
        if(path[i] == '/' || path[i] == '\\')
        {
            pos= i;
            cdepth++;
        }
    }
    // return result
    if(cdepth != depth)
        return path;
    else
        return path.substr(0,pos);
}

//*****************************************************************************
//  Method: VisWinPathTracker::GetCommonPath
//
//  Purpose:
//    Gets the common path prefix from a list of paths
//
//  Programmer: Cyrus Harrison
//  Creation:   October 1, 2007
//
// ****************************************************************************

std::string VisWinPathTracker::GetCommonPath(stringVector &paths)
{

    // loop indices
    int i = 0;
    // get # of paths
    int npaths = paths.size();
    
    // handle simple cases
    if(npaths == 0)
        return "";
    if(npaths == 1)
        return paths[0];
        
    std::string res = paths[0];
    std::string r_sub,p_sub;
    
    for( i=1; i<npaths; i++)
    {
        // if paths are exactly the same skip
        if(paths[i] == res)
            continue;
        
        int depth = 1;
            
        bool done = false;
        while( !done )
        {
            // get subpaths
            r_sub = GetSubPath(res,depth);
            p_sub = GetSubPath(paths[i],depth);

            // if they are equal continue
            if(r_sub == p_sub)
            {
                depth++;
            }
            else
            {
                // the common path the last sucessful depth
                done = true;
                res = GetSubPath(res,depth-1);
            }
        }
    }
    return res;
}

// ****************************************************************************
//  Method: VisWinPathTracker::Entry constructor
//
//  Programmer: Cyrus Harrison
//  Creation:   Sunday June 17, 2007
//
// ****************************************************************************


VisWinPathTracker::Entry::Entry()
{
    refCount  = 0;
    fullPath  = "";
    smartPath = "";
}

// ****************************************************************************
//  Method: VisWinPathTracker::Entry constructor
//
//  Programmer: Cyrus Harrison
//  Creation:   Sunday June 17, 2007
//
//    Cyrus Harrison, Tue Sep 25 10:57:57 PDT 2007
//    Added init of fileName and directory
//
//    Kathleen Bonnell, Thu Dec  6 09:34:01 PST 2007 
//    Changed "/" to SLASH_STRING for windows compatibility. 
//
//    Kathleen Bonnell, Wed Feb 13 07:52:58 PST 2008 
//    Call FindLastSlash, which searches for unix and windows style slashes.
//
// ****************************************************************************


VisWinPathTracker::Entry::Entry(const std::string &path)
{
    refCount  = 1;
    fullPath  = path;
    smartPath = path;
    directory = path;
    fileName  = path;
    
    int idx = FindLastSlash(directory);
    if((size_t)idx != string::npos)
    {
        fileName  = directory.substr(idx+1);
        directory = directory.substr(0,idx);
    }   
    smartDir  = directory;
}

// ****************************************************************************
//  Method: VisWinPathTracker::Entry destructor
//
//  Programmer: Cyrus Harrison
//  Creation:   Sunday June 17, 2007
//
// ****************************************************************************


VisWinPathTracker::Entry::~Entry()
{
    ;
}

